#ifndef IBOX_EVENT_SUBSCRIVER_H_INCLUDED
#define IBOX_EVENT_SUBSCRIVER_H_INCLUDED

#if !defined(WIN32)
#	include <errno.h>
#else
#	include <windows.h>
#endif

#include <string>
#include <boost/shared_ptr.hpp>

namespace BoxCore {

/**
 * The BoxErrorStruct 
 */
struct BoxErrorStruct {
	bool isKnownError;				// If set to true errorCode containt IBoxEventSubscriber::KnownErrorStates value, otherwise errno|GetLastError
	int errorCode;					// See isKnownError for details
	std::string mutable message;	// Error message string 
};

/**
 * The IBoxEventSubscriber NOTE All implementation must be exception safe, and take min. execution time
 */
class IBoxEventSubscriber {
public :
	enum KnownErrorStates {
		OUT_OF_ORDER = 0x0,
		INTERNAL_ERROR = 0x1,
		CLIENT_DROP_SESSION = 0x2,
		SERVER_UNEXPECTED_DROP_SESSION = 0x3
	};

	IBoxEventSubscriber() { }
	virtual ~IBoxEventSubscriber() { }
	
	virtual void registred() = 0;
	virtual void onWorkerStart(int slotId) = 0;
	virtual void onWorkerComplete(int slotId) = 0;
	virtual void onWorkerCompleteWithError(int slotId, BoxErrorStruct const & boxError) = 0;
	
};

typedef boost::shared_ptr<IBoxEventSubscriber> IBoxEventSubscriberPtr;

/**
 * Helpers
 */

template <class InputIterator, class NotifyFunctor>
static void notifySubsctibers(InputIterator first, InputIterator last, NotifyFunctor functor) 
{
	for (;first != last; ++first) 
		functor((*first).get());
}

static inline BoxErrorStruct constructBoxError(std::string const & message, int errorCode) 
{
	BoxErrorStruct errorStruct;
	if (errorCode == IBoxEventSubscriber::INTERNAL_ERROR) {
		errorStruct.isKnownError = false;
//TODO Replace if-def to pimpl
#if !defined(WIN32)
		errorStruct.errorCode = (int)errno;
#else
		errorStruct.errorCode = (int)GetLastError();
#endif // WIN32
	}
	errorStruct.message = message;
	return errorStruct;
}

template <class InputIterator>
static void notifySubsctibersOnError(
	InputIterator first, InputIterator last, int slotId, std::string const & message, int errorCode) 
{	
	for (;first != last; ++first)
		(*first)->onWorkerCompleteWithError(slotId, constructBoxError(message, errorCode));
}

} // namespace BoxCore

#endif

